package de.sendorian.app.forumArchive;


import java.io.IOException;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;

import de.sendorian.app.forumArchive.domain.Forum;
import de.sendorian.app.forumArchive.domain.Post;
import de.sendorian.app.forumArchive.domain.Topic;
import de.sendorian.app.forumArchive.download.Downloader;
import de.sendorian.app.forumArchive.finder.Finder;
import de.sendorian.app.forumArchive.renamer.FileRenamer;
import de.sendorian.app.forumArchive.repository.ForumRepository;
import de.sendorian.db.DAO;

/**
 * Central class that manages the indexing and downloading.
 *
 * @author sendorian
 *
 */
public class ArchiveBuilder {

	private static Logger logger = Logger.getLogger(ArchiveBuilder.class.getName());


	public static void main(String[] args) throws IOException {
		Config config = Config.get();
		ForumRepository forumRepository = new ForumRepository();

		Finder<Forum, Topic> topicFinder = config.getTopicFinder().withFilter(
		    config.getTopicMatcher());

		List<Forum> forums = config.getForumFinder().find(config.getBaseUrl());
		for (Forum forum : forums) {
			Forum persistenForum = forumRepository.findByName(forum.getName());
			if (persistenForum != null) {
				forum = persistenForum;
			}

			logger.log(Level.INFO, "Finding topics for forum \"" + forum.getName() + "\"");
			List<Topic> topics = topicFinder.find(forum);

			for (Topic topic : topics) {
				if (hasToBeUpdated(forum, topic)) {
					updateTopic(topic, forum);
				}
			}
			DAO.saveOrUpdate(forum);
		}

		forums = new ForumRepository().findAll();
		FileRenamer renamer = config.getDefaultRenamer();
		new Downloader(config.getPostMatcher(), config.getTopicMatcher(), renamer)
		    .downloadForums(forums);

		DAO.shutdownDatabase();
		logger.info("Finished!");
	}


	/**
	 * Returns true if the topic is not yet persistent or outdated.
	 *
	 */
	private static boolean hasToBeUpdated(@Nonnull final Forum forum, @Nullable final Topic topic) {
		Predicate<Topic> uptodatePredicate = new Predicate<Topic>() {
			public boolean apply(Topic persistentTopic) {
				if (!persistentTopic.equals(topic)) {
					return false;
				}

				DateTime persistentLastChange = new DateTime(persistentTopic.getLastPost());
				DateTime onlineLastChange = new DateTime(topic.getLastPost());
				if (onlineLastChange.isAfter(persistentLastChange)) {
					logger.debug("Topic \"" + topic.getName() + "\" is outdated (DB: "
					    + persistentLastChange + ", Online: " + onlineLastChange + ").");
					return false;
				}
				logger.info("Topic \"" + topic.getName() + "\" is uptodate.");
				return true;
			}
		};
		return !Iterators.any(forum.getTopics().iterator(), uptodatePredicate);
	}


	/**
	 * Updates the outdated Topic in the forum or adds it to the forums' topics if it was not
	 * persisted before.
	 *
	 */
    private static void updateTopic(@Nonnull Topic topic, @Nonnull Forum forum) {
	    logger.info("Updating posts for topic \"" + topic.getName() + "\"");
	    Finder<Topic, Post> postFinder = Config.get().getPostFinder();
	    List<Post> posts = postFinder.find(topic);
	    topic.setPosts(posts);
	    forum.getTopics().remove(topic);
		forum.getTopics().add(topic);
    }
}
